using System;
using System.Threading;
using System.Runtime.CompilerServices;


namespace FibonacciA
{
	class MainClass
	{
		private int currentElements;
		
		public static void Main(string[] args)
		{
			CircularBuffer cb = new CircularBuffer(25);
			Producer producer = new Producer(cb);
			Consumer consumer = new Consumer(cb);
			ThreadStart producerJob = new ThreadStart(producer.produce);
			Thread producerThread = new Thread (producerJob);
			producerThread.Start();
			ThreadStart consumerJob = new ThreadStart(consumer.consume);
			Thread consumerThread = new Thread (consumerJob);
			consumerThread.Start();
			while(true);
		}
	}
	
	class CircularBuffer
	{
		private object[] buffer;
		private int maxSize=0, inValue=0, outValue=0;
		public int numElements;
		public static readonly object countLock = new object();
		
		public CircularBuffer(int numElements)
		{
			maxSize = numElements;
			buffer = new object[maxSize];
		}
		
		public object getElement()
		{
			object read = buffer[outValue];
			outValue++;
			if (outValue==maxSize) outValue=0;
			return read;
		}
		
		public void putElement(object element)
		{
			buffer[inValue] = element;
			inValue++;
			if (inValue==maxSize) inValue=0;
		}
		
		public int getMaxSize()
		{
			return maxSize;	
		}
	}
	
	class Producer
	{
		private long lastFibonacci=0;
		private long currentFibonacci=1;
		private long nextFibonacci;
		private CircularBuffer cb;
		
		public Producer(CircularBuffer cb)
		{
			this.cb = cb;
		}
		
		public void produce()
		{
			Random r = new Random();
			
			while(true)
			{
				System.Threading.Thread.Sleep(r.Next(1000));
				lock (CircularBuffer.countLock)
				{
					if (cb.numElements < cb.getMaxSize()) 
					{
						nextFibonacci = currentFibonacci + lastFibonacci;
						cb.putElement(nextFibonacci);
						lastFibonacci = currentFibonacci;
						currentFibonacci = nextFibonacci;
						cb.numElements++;
					}
				}
			}
		}
		
		
	}
	
	class Consumer
	{
		private CircularBuffer cb;
		
		public Consumer(CircularBuffer cb)
		{
			this.cb = cb;
		}
		
		public void consume()
		{
			Random r = new Random();
			while(true)
			{
				System.Threading.Thread.Sleep(r.Next(1000));
				lock (CircularBuffer.countLock)
				{
					if (cb.numElements > 0) 
					{
						Console.WriteLine("Selected fibonacci element: {0}", cb.getElement());
						cb.numElements--;
					}
				}
			}
		}
	}
}